package com.jsftoolkit.gen.writer;

import java.io.PrintStream;
import java.lang.reflect.Modifier;
import java.util.LinkedHashMap;
import java.util.Set;

/**
 * Interface for classes that generate class code. Implementations may assume
 * that methods will be called in the expected order, e.g.
 * {@link #printPackage(String)} will be called first, then
 * {@link #printImports(Set)}, etc.
 * <p>
 * ClassWriter implementations will generally be 'smarter' than a typical Writer
 * or output device. They are expected to examine the information they are given
 * and take appropriate steps (if possible) to ensure that a compilable java
 * file is generated.
 * <p>
 * This means that they may interpret and take actions not specifically
 * specified by the interface. The scope of these actions should be limited to
 * those actions that affect the usability of the generated code. It is not the
 * job of the writer to determine a specific behavior (although a particular
 * writer may have certain behavioral characteristics). e.g. a writer may append
 * unspecified utility functions to the class that are necessary for the
 * implementation it provides. Likewise, the writer may rename and alter the
 * internals of the class as long as the public interface remains the same.
 * <p>
 * A writer must not attempt to determine the desired behavior of the generated
 * class based on input received through methods of this interface, however it
 * may be configured (via its non-interface methods/properties) beforehand to
 * produce a certain behavior.
 * 
 * @author noah
 * 
 */
public interface ClassWriter {

	/**
	 * Prints the package declaration.
	 * 
	 * @param _package
	 */
	void printPackage(String _package);

	/**
	 * Print import statements for the given classes (if necessary).
	 * 
	 * @param imports
	 *            the classes to import. Implementations are free to modify the
	 *            given Set however they wish.
	 */
	void printImports(Set<Class<?>> imports);

	/**
	 * Print the javadoc comment before a class. Implementations may not print
	 * anything.
	 * 
	 */
	void printClassComment();

	/**
	 * Begins a field declaration. e.g. ' private String foo'. Does not include
	 * a newline or ';' but does indent before the declaration.
	 * 
	 * @param mods
	 *            the modifiers
	 * @param type
	 *            the class of the field
	 * @param name
	 *            the name of the field. Will not be altered, so client code
	 *            should call {@link #getFieldName(String)} first if it may be
	 *            an invalid field name.
	 */
	void startField(int mods, Class<?> type, String name);

	/**
	 * Prints a constant declaration. Ends with a newline.
	 * 
	 * @param type
	 *            the type of the constant
	 * @param name
	 *            the name of the constant
	 * @param value
	 *            the value of the constant
	 */
	void printConstant(Class<?> type, String name, CharSequence value);

	/**
	 * Prints the class declaration and opening bracket. e.g. 'public abstract
	 * class Foo extends Bar implements Baz, Qux {\n'
	 * <p>
	 * This implementation delegates to {@link #printModifiers(boolean)} and
	 * resolves class names via {@link #getClassName(Class)}.
	 * 
	 * @param mods
	 * @param className
	 * @param interfaces
	 * @param superClass
	 */
	void printDeclaration(int mods, String className, Class<?> superClass,
			Iterable<Class<?>> interfaces);

	/**
	 * Prints the class modifiers (e.g. public final ...). There will always be
	 * a space following any modifiers printed (unless none are printed).
	 * 
	 * @param mod
	 *            the modifiers
	 * @see Modifier
	 */
	void printModifiers(int mod);

	/**
	 * Convenience method. Should produce a declaration equivalent to
	 * <code>startMethod(mods, null, className, params)</code>, although
	 * implementations may add additional content.
	 */
	void startConstructor(int mods, LinkedHashMap<String, Class<?>> params,
			Class<? extends Throwable>... exceptions);

	/**
	 * Starts a method/constructor declaration. Assumes that it is positioned at
	 * the beginning of a new line. May or may not provide a method comment.
	 * After calling, the writer will be a the start of a new line and the
	 * indent level should have been increased.
	 * 
	 * @param mods
	 *            the modifiers. See {@link #printModifiers(int)}
	 * @param returnType
	 *            the return type. null for no return type (constructor).
	 * @param name
	 *            the method name
	 * @param params
	 *            the method parameters or null for no parameters (an empty map
	 *            is also allowed)
	 */
	void startMethod(int mods, Class<?> returnType, String name,
			LinkedHashMap<String, Class<?>> params,
			Class<? extends Throwable>... exceptions);

	/**
	 * Terminates a block level element. Assumes that we are at the end of a
	 * line. The indent level will be reduced.
	 */
	void endBlock();

	/**
	 * @see ClassWriter#printType(Class)
	 * 
	 * @author noah
	 * 
	 * @param <T>
	 */
	public static interface TypeParameter<T> {
	}

	/**
	 * Prints a type. Parameterized/Generic types may be specified by passing in
	 * the value returned from {@link #getClass()} on an anonymous
	 * implementation, e.g.<code><br/>
	 * Class type = new HashMap&lt;String,String> () {<br/>
	 * }.getClass();<br/>
	 * </code>
	 * <p>
	 * You can also use {@link TypeParameter}, which is handy for cases where
	 * you would otherwise have to implement methods or the class is final. e.g.
	 * <code>new TypeParameter&lt;Class&lt;? extends Foo>>(){}.getClass()</code>
	 * and the parameter will be interpreted as the type. In this example
	 * <code>Class&lt;? extends Foo></code>.
	 * <p>
	 * If you need an array of a class, you can pass
	 * <code>java.lang.reflect.Array.newInstance(theClass,0).getClass()</code>.
	 * 
	 * @param c
	 *            the type to print. null is ignored (nothing is printed).
	 */
	void printType(Class<?> c);

	/**
	 * Starts a block. i.e. '{'. Calling this method should increase the indent
	 * level. After this method finishes, the writer should be at the start of a
	 * new line.
	 */
	void startBlock();

	/**
	 * Writes the closing class bracket and closes the {@link PrintStream}.
	 * Implementations may do additional things.
	 */
	void close();

	/**
	 * Indents at the current indent level. Should always be called at the
	 * beginning of a line.
	 * 
	 * @return this
	 */
	ClassWriter indent();

	/**
	 * @see PrintStream#printf(String, Object[])
	 * 
	 * @param format
	 * @param args
	 */
	ClassWriter printf(String format, Object... args);

	ClassWriter print(char c);

	ClassWriter print(CharSequence s);

	ClassWriter println(CharSequence s);

	/**
	 * Creates a getter/setter pair. Implementors should resolve the
	 * propertyName to a field name via {@link #getFieldName(String)}.
	 * 
	 * @param propertyName
	 * @param propertyClass
	 */
	void printPropertyAccessors(final String propertyName,
			final Class<?> propertyClass);

	/**
	 * Ensures that the given field name is valid.
	 * 
	 * @param name
	 *            the original field name. Assumes no whitespace or illegal
	 *            characters in the name (but may be a reserved word).
	 * @return the name that will be used for the field.
	 */
	String getFieldName(String name);

}